Try using a Targetted Gene Correlation Network to identify the LFY1 and LFY2 networks

Install…

BiocManager::install(c("AnnotationDBi", "GO.db", "preprocessCore", "impute", "rrvgo", "ComplexHeatmap"))
BiocManager::install(c("sva", "GOSim"))
install.packages("MLmetrics")
remotes::install_local("~/Downloads/GOSim_1.40.0.tgz") # get download link from https://bioconductor.org/packages/3.18/bioc/html/GOSim.html
remotes::install_github('juanbot/CoExpNets') 
# remotes::install_github("aliciagp/TGCN") # don't use this, use mine, see below!

use my modified version of TGCN

# remotes::install_local("~/git/TGCN/", force = TRUE)
# or from web:
remotes::install_github("https://github.com/jnmaloof/TGCN", ref = "dev")
Skipping install of 'TGCN' from a github remote, the SHA1 (6985dd91) has not changed since last install.
  Use `force = TRUE` to force installation
library(TGCN)
library(tidyverse)
── Attaching core tidyverse packages ────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.4     ── Conflicts ──────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(ggpubr)
library(gridExtra)

Attaching package: ‘gridExtra’

The following object is masked from ‘package:dplyr’:

    combine
library(gplots)

Attaching package: ‘gplots’

The following object is masked from ‘package:stats’:

    lowess
library(patchwork)
sample.description <- read.csv("../input/sample.description.csv")

load read counts. These should be log2 cpm or Voom transformed or something similar.

lcpm <- read.csv("../output/log2cpm.csv.gz", row.names = 1, check.names = FALSE) 
rownames(lcpm) <- str_remove(rownames(lcpm), fixed(".v2"))
head(lcpm)
dim(lcpm)
[1] 26704    48

Get the GO info and convert to a list.

GOs <- read_delim("../input/Crichardii_676_v2.1.annotation_info.txt")
GOs <- GOs[match(rownames(lcpm), GOs$transcriptName),] %>% drop_na(transcriptName)
gene2GO.list <- GOs %>% 
  select(transcriptName, GO) %>%
  rowwise() %>%
  mutate(GOlist = str_split(GO, pattern = " ")) %>%
  ungroup() %>%
  mutate(GOlist = set_names(GOlist, transcriptName)) %>%
  pull(GOlist)

Looking at the example notebook on the github site, it looks like genes in rows, samples in columns

My genes of interest.

CrLFY1 <- "Ceric.33G031700"

CrLFY2 <- "Ceric.18G076300"

Subset data to build LFY1 and LFY2 networks. Want to find networks around expression of LFY1 or LFY2, so pull those out and also create matrices that don’t have them.

LFY1.expression <- lcpm[str_detect(rownames(lcpm), CrLFY1),] %>%
  unlist() %>%
  as.vector()

input_for_LFY1 <- lcpm[str_detect(rownames(lcpm), CrLFY1, negate = TRUE),]

LFY2.expression <- lcpm[str_detect(rownames(lcpm), CrLFY2),] %>%
  unlist() %>%
  as.vector()

input_for_LFY2 <- lcpm[str_detect(rownames(lcpm), CrLFY2, negate = TRUE),]

LFY1 network

if(dir.exists("../output/TGCN_LFY1"))   system("rm -r ../output/TGCN_LFY1")

if(!dir.exists("../output/TGCN_LFY1"))  dir.create("../output/TGCN_LFY1")

r.lfy1 <- testAllCutoffs(exprData=input_for_LFY1,
                    target=LFY1.expression,
                    covs=NULL,
                    train.split=0.7,
                    nfolds=5,
                    t=10,
                    path="../output/TGCN_LFY1",
                    targetName="LFY1",
                    tissueName="ALL",
                    seed=3333,
                    cutoffs=10:1,
                    n=100, 
                    m=10, 
                    s=10, 
                    minCor=0.3,
                    maxTol=3,
                    save=T,
                    overwrite=T,
                    approach="enrichment", # the approach selected to complete the seed modules
                    report=F,              # if report=T, an automated report will be created
                    gene2GO=gene2GO.list,
                    cellTypeAnnotation=FALSE)
- Step 1: Create a linear regression model for each gene set of hubs based on their ratio of appearance 
Apply LASSO 10 times for feature selection 
Warning: No enrichment can pe performed - there are no feasible GO terms!Warning: No enrichment can pe performed - there are no feasible GO terms!Warning: No enrichment can pe performed - there are no feasible GO terms!
Number of hubs per ratio of appearance
cutoff10  cutoff9  cutoff8  cutoff7  cutoff6  cutoff5  cutoff4  cutoff3  cutoff2  cutoff1 
       1        1        2        4        5       10       11       15       40      150 
Cutoffs selected are 8 7 6 5 4 3 2 1 
Get final model and the cv error for cutoff 8 
Get final model and the cv error for cutoff 7 
Get final model and the cv error for cutoff 6 
Get final model and the cv error for cutoff 5 
Get final model and the cv error for cutoff 4 
Get final model and the cv error for cutoff 3 
Get final model and the cv error for cutoff 2 
Get final model and the cv error for cutoff 1 

- Step 2: represent train and test error per ratio of appearance model

- Step 3: creating a network for each ratio of appearance where the number of hubs is <=30

 Step 3.1: TGCN creation for ratio of appearance 8 

 Step 3.2: TGCN characterization for cutoff 8 
Warning: No enrichment can pe performed - there are no feasible GO terms!

 Step 3.1: TGCN creation for ratio of appearance 7 

 Step 3.2: TGCN characterization for cutoff 7 

 Step 3.1: TGCN creation for ratio of appearance 6 

 Step 3.2: TGCN characterization for cutoff 6 

 Step 3.1: TGCN creation for ratio of appearance 5 

 Step 3.2: TGCN characterization for cutoff 5 

 Step 3.1: TGCN creation for ratio of appearance 4 

 Step 3.2: TGCN characterization for cutoff 4 

 Step 3.1: TGCN creation for ratio of appearance 3 

 Step 3.2: TGCN characterization for cutoff 3 
r.lfy1$selectRatio$nHubs + r.lfy1$selectRatio$stats

Focus on 5 and 6

p <- lapply(r.lfy1$nets, function(cutoff) cutoff$GOenrich$plotStats) 

ggarrange(p$c5 + theme(text=element_text(size=10)), 
          p$c6 + theme(text=element_text(size=10)), 
          ncol=2, nrow=1, common.legend=T, legend="bottom")

r.lfy1$nets$c5$net$moduleSizeSelectionPlot

r.lfy1$nets$c6$net$moduleSizeSelectionPlot

Correlation with trait, I assume

r.lfy1$nets$c5$net$plotCorr

r.lfy1$nets$c6$net$plotCorr

DT::datatable(r.lfy1$nets$c5$net$modules)
DT::datatable(r.lfy1$nets$c6$net$modules)
knitr::include_graphics("../output/TGCN_LFY1/results/LFY1_ALL_c5_TGCN_crossTabPlot.png")

knitr::include_graphics("../output/TGCN_LFY1/results/LFY1_ALL_c6_TGCN_crossTabPlot.png")

grid.arrange(r.lfy1$nets$c5$GOenrich$plotStats, r.lfy1$nets$c5$GOenrich$plotNterms, nrow=2)

grid.arrange(r.lfy1$nets$c6$GOenrich$plotStats, r.lfy1$nets$c6$GOenrich$plotNterms, nrow=2)

LFY2 network

if(dir.exists("../output/TGCN_LFY2"))   system("rm -r ../output/TGCN_LFY2")

if(!dir.exists("../output/TGCN_LFY2"))  dir.create("../output/TGCN_LFY2")

r.lfy2 <- testAllCutoffs(exprData=input_for_LFY2,
                    target=LFY2.expression,
                    covs=NULL,
                    train.split=0.7,
                    nfolds=5,
                    t=10,
                    path="../output/TGCN_LFY2",
                    targetName="LFY2",
                    tissueName="ALL",
                    seed=3333,
                    cutoffs=10:1,
                    n=100, 
                    m=10, 
                    s=10, 
                    minCor=0.3,
                    maxTol=3,
                    save=T,
                    overwrite=T,
                    approach="enrichment", # the approach selected to complete the seed modules
                    report=F,              # if report=T, an automated report will be created
                    gene2GO=gene2GO.list,
                    cellTypeAnnotation=FALSE)
- Step 1: Create a linear regression model for each gene set of hubs based on their ratio of appearance 
Apply LASSO 10 times for feature selection 
Number of hubs per ratio of appearance
cutoff10  cutoff9  cutoff8  cutoff7  cutoff6  cutoff5  cutoff4  cutoff3  cutoff2  cutoff1 
       0        0        1        1        2        2        5       15       39      152 
Cutoffs selected are 6 5 4 3 2 1 
Get final model and the cv error for cutoff 6 
Get final model and the cv error for cutoff 5 
Get final model and the cv error for cutoff 4 
Get final model and the cv error for cutoff 3 
Get final model and the cv error for cutoff 2 
Get final model and the cv error for cutoff 1 

- Step 2: represent train and test error per ratio of appearance model

- Step 3: creating a network for each ratio of appearance where the number of hubs is <=30

 Step 3.1: TGCN creation for ratio of appearance 6 

 Step 3.2: TGCN characterization for cutoff 6 

 Step 3.1: TGCN creation for ratio of appearance 5 

 Step 3.2: TGCN characterization for cutoff 5 

 Step 3.1: TGCN creation for ratio of appearance 4 

 Step 3.2: TGCN characterization for cutoff 4 

 Step 3.1: TGCN creation for ratio of appearance 3 

 Step 3.2: TGCN characterization for cutoff 3 

Focus on 3

p <- lapply(r.lfy2$nets, function(cutoff) cutoff$GOenrich$plotStats) 

ggarrange(p$c3 + theme(text=element_text(size=10)), 
          p$c4 + theme(text=element_text(size=10)), 
          ncol=2, nrow=1, common.legend=T, legend="bottom")

r.lfy2$nets$c3$net$moduleSizeSelectionPlot

Correlation with trait, I assume

r.lfy2$nets$c3$net$plotCorr

DT::datatable(r.lfy2$nets$c3$net$modules)
knitr::include_graphics("../output/TGCN_LFY2/results/LFY2_ALL_c3_TGCN_crossTabPlot.png")

grid.arrange(r.lfy2$nets$c3$GOenrich$plotStats, r.lfy2$nets$c3$GOenrich$plotNterms, nrow=2)

Heat Maps

LFY1

C5

LFY1.c5 <- read_csv("../output/TGCN_LFY1/results/LFY1_ALL_c5_TGCN.csv")
Rows: 160 Columns: 3── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): hubGene, genes
dbl (1): cor
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
LFY1.c5

All genes

rbind(LFY1=LFY1.expression,lcpm[unique(LFY1.c5$genes),]) %>%
  as.matrix() %>%
  heatmap.2(trace="none", cexRow= 0.6, cexCol=0.7, col="bluered", scale="row")

hubgenes

rbind(LFY1=LFY1.expression,lcpm[unique(LFY1.c5$hubGene),]) %>%
  as.matrix() %>%
  heatmap.2(trace="none", cexRow= 0.6, cexCol=0.7, col="bluered", scale="row")

C6

LFY1.c6 <- read_csv("../output/TGCN_LFY1/results/LFY1_ALL_c6_TGCN.csv")
Rows: 70 Columns: 3── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): hubGene, genes
dbl (1): cor
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
LFY1.c6

All genes

hubgenes

rbind(LFY1=LFY1.expression,lcpm[unique(LFY1.c6$hubGene),]) %>%
  as.matrix() %>%
  heatmap.2(trace="none", cexRow= 0.6, cexCol=0.7, col="bluered", scale="row")

LFY2 C3

LFY2.c3 <- read_csv("../output/TGCN_LFY2/results/LFY2_ALL_c3_TGCN.csv")
Rows: 310 Columns: 3── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): hubGene, genes
dbl (1): cor
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
LFY2.c3

All genes

hubgenes

rbind(LFY2=LFY2.expression,lcpm[unique(LFY2.c3$hubGene),]) %>%
  as.matrix() %>%
  heatmap.2(trace="none", cexRow= 0.6, cexCol=0.7, col="bluered", scale="row")

LS0tCnRpdGxlOiAiMDZfVEdDTiIKYXV0aG9yOiAiSnVsaW4gTWFsb29mIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKVHJ5IHVzaW5nIGEgW1RhcmdldHRlZCBHZW5lIENvcnJlbGF0aW9uIE5ldHdvcmtdKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNTk4LTAyNC02NzMyOS03KSB0byBpZGVudGlmeSB0aGUgTEZZMSBhbmQgTEZZMiBuZXR3b3JrcwoKSW5zdGFsbC4uLgpgYGB7ciwgZXZhbD1GQUxTRX0KQmlvY01hbmFnZXI6Omluc3RhbGwoYygiQW5ub3RhdGlvbkRCaSIsICJHTy5kYiIsICJwcmVwcm9jZXNzQ29yZSIsICJpbXB1dGUiLCAicnJ2Z28iLCAiQ29tcGxleEhlYXRtYXAiKSkKQmlvY01hbmFnZXI6Omluc3RhbGwoYygic3ZhIiwgIkdPU2ltIikpCmluc3RhbGwucGFja2FnZXMoIk1MbWV0cmljcyIpCnJlbW90ZXM6Omluc3RhbGxfbG9jYWwoIn4vRG93bmxvYWRzL0dPU2ltXzEuNDAuMC50Z3oiKSAjIGdldCBkb3dubG9hZCBsaW5rIGZyb20gaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzLzMuMTgvYmlvYy9odG1sL0dPU2ltLmh0bWwKcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoJ2p1YW5ib3QvQ29FeHBOZXRzJykgCiMgcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImFsaWNpYWdwL1RHQ04iKSAjIGRvbid0IHVzZSB0aGlzLCB1c2UgbWluZSwgc2VlIGJlbG93IQpgYGAKCnVzZSBteSBtb2RpZmllZCB2ZXJzaW9uIG9mIFRHQ04KYGBge3J9CiMgcmVtb3Rlczo6aW5zdGFsbF9sb2NhbCgifi9naXQvVEdDTi8iLCBmb3JjZSA9IFRSVUUpCiMgb3IgZnJvbSB3ZWI6CnJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJodHRwczovL2dpdGh1Yi5jb20vam5tYWxvb2YvVEdDTiIsIHJlZiA9ICJkZXYiKQpgYGAKCgpgYGB7cn0KbGlicmFyeShUR0NOKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGdwbG90cykKbGlicmFyeShwYXRjaHdvcmspCmBgYAoKYGBge3J9CnNhbXBsZS5kZXNjcmlwdGlvbiA8LSByZWFkLmNzdigiLi4vaW5wdXQvc2FtcGxlLmRlc2NyaXB0aW9uLmNzdiIpCmBgYAoKCmxvYWQgcmVhZCBjb3VudHMuIFRoZXNlIHNob3VsZCBiZSBsb2cyIGNwbSBvciBWb29tIHRyYW5zZm9ybWVkIG9yIHNvbWV0aGluZyBzaW1pbGFyLgoKYGBge3J9CmxjcG0gPC0gcmVhZC5jc3YoIi4uL291dHB1dC9sb2cyY3BtLmNzdi5neiIsIHJvdy5uYW1lcyA9IDEsIGNoZWNrLm5hbWVzID0gRkFMU0UpIApyb3duYW1lcyhsY3BtKSA8LSBzdHJfcmVtb3ZlKHJvd25hbWVzKGxjcG0pLCBmaXhlZCgiLnYyIikpCmhlYWQobGNwbSkKZGltKGxjcG0pCmBgYApHZXQgdGhlIEdPIGluZm8gYW5kIGNvbnZlcnQgdG8gYSBsaXN0LgpgYGB7cn0KR09zIDwtIHJlYWRfZGVsaW0oIi4uL2lucHV0L0NyaWNoYXJkaWlfNjc2X3YyLjEuYW5ub3RhdGlvbl9pbmZvLnR4dCIpCkdPcyA8LSBHT3NbbWF0Y2gocm93bmFtZXMobGNwbSksIEdPcyR0cmFuc2NyaXB0TmFtZSksXSAlPiUgZHJvcF9uYSh0cmFuc2NyaXB0TmFtZSkKZ2VuZTJHTy5saXN0IDwtIEdPcyAlPiUgCiAgc2VsZWN0KHRyYW5zY3JpcHROYW1lLCBHTykgJT4lCiAgcm93d2lzZSgpICU+JQogIG11dGF0ZShHT2xpc3QgPSBzdHJfc3BsaXQoR08sIHBhdHRlcm4gPSAiICIpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKEdPbGlzdCA9IHNldF9uYW1lcyhHT2xpc3QsIHRyYW5zY3JpcHROYW1lKSkgJT4lCiAgcHVsbChHT2xpc3QpCmBgYAoKTG9va2luZyBhdCB0aGUgZXhhbXBsZSBub3RlYm9vayBvbiB0aGUgZ2l0aHViIHNpdGUsIGl0IGxvb2tzIGxpa2UgZ2VuZXMgaW4gcm93cywgc2FtcGxlcyBpbiBjb2x1bW5zCgpNeSBnZW5lcyBvZiBpbnRlcmVzdC4KYGBge3J9CkNyTEZZMSA8LSAiQ2VyaWMuMzNHMDMxNzAwIgoKQ3JMRlkyIDwtICJDZXJpYy4xOEcwNzYzMDAiCmBgYAoKU3Vic2V0IGRhdGEgdG8gYnVpbGQgTEZZMSBhbmQgTEZZMiBuZXR3b3Jrcy4gIFdhbnQgdG8gZmluZCBuZXR3b3JrcyBhcm91bmQgZXhwcmVzc2lvbiBvZiBMRlkxIG9yIExGWTIsIHNvIHB1bGwgdGhvc2Ugb3V0IGFuZCBhbHNvIGNyZWF0ZSBtYXRyaWNlcyB0aGF0IGRvbid0IGhhdmUgdGhlbS4KYGBge3J9CkxGWTEuZXhwcmVzc2lvbiA8LSBsY3BtW3N0cl9kZXRlY3Qocm93bmFtZXMobGNwbSksIENyTEZZMSksXSAlPiUKICB1bmxpc3QoKSAlPiUKICBhcy52ZWN0b3IoKQoKaW5wdXRfZm9yX0xGWTEgPC0gbGNwbVtzdHJfZGV0ZWN0KHJvd25hbWVzKGxjcG0pLCBDckxGWTEsIG5lZ2F0ZSA9IFRSVUUpLF0KCkxGWTIuZXhwcmVzc2lvbiA8LSBsY3BtW3N0cl9kZXRlY3Qocm93bmFtZXMobGNwbSksIENyTEZZMiksXSAlPiUKICB1bmxpc3QoKSAlPiUKICBhcy52ZWN0b3IoKQoKaW5wdXRfZm9yX0xGWTIgPC0gbGNwbVtzdHJfZGV0ZWN0KHJvd25hbWVzKGxjcG0pLCBDckxGWTIsIG5lZ2F0ZSA9IFRSVUUpLF0KYGBgCgojIyBMRlkxIG5ldHdvcmsKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmlmKGRpci5leGlzdHMoIi4uL291dHB1dC9UR0NOX0xGWTEiKSkgICBzeXN0ZW0oInJtIC1yIC4uL291dHB1dC9UR0NOX0xGWTEiKQoKaWYoIWRpci5leGlzdHMoIi4uL291dHB1dC9UR0NOX0xGWTEiKSkgIGRpci5jcmVhdGUoIi4uL291dHB1dC9UR0NOX0xGWTEiKQoKci5sZnkxIDwtIHRlc3RBbGxDdXRvZmZzKGV4cHJEYXRhPWlucHV0X2Zvcl9MRlkxLAogICAgICAgICAgICAgICAgICAgIHRhcmdldD1MRlkxLmV4cHJlc3Npb24sCiAgICAgICAgICAgICAgICAgICAgY292cz1OVUxMLAogICAgICAgICAgICAgICAgICAgIHRyYWluLnNwbGl0PTAuNywKICAgICAgICAgICAgICAgICAgICBuZm9sZHM9NSwKICAgICAgICAgICAgICAgICAgICB0PTEwLAogICAgICAgICAgICAgICAgICAgIHBhdGg9Ii4uL291dHB1dC9UR0NOX0xGWTEiLAogICAgICAgICAgICAgICAgICAgIHRhcmdldE5hbWU9IkxGWTEiLAogICAgICAgICAgICAgICAgICAgIHRpc3N1ZU5hbWU9IkFMTCIsCiAgICAgICAgICAgICAgICAgICAgc2VlZD0zMzMzLAogICAgICAgICAgICAgICAgICAgIGN1dG9mZnM9MTA6MSwKICAgICAgICAgICAgICAgICAgICBuPTEwMCwgCiAgICAgICAgICAgICAgICAgICAgbT0xMCwgCiAgICAgICAgICAgICAgICAgICAgcz0xMCwgCiAgICAgICAgICAgICAgICAgICAgbWluQ29yPTAuMywKICAgICAgICAgICAgICAgICAgICBtYXhUb2w9MywKICAgICAgICAgICAgICAgICAgICBzYXZlPVQsCiAgICAgICAgICAgICAgICAgICAgb3ZlcndyaXRlPVQsCiAgICAgICAgICAgICAgICAgICAgYXBwcm9hY2g9ImVucmljaG1lbnQiLCAjIHRoZSBhcHByb2FjaCBzZWxlY3RlZCB0byBjb21wbGV0ZSB0aGUgc2VlZCBtb2R1bGVzCiAgICAgICAgICAgICAgICAgICAgcmVwb3J0PUYsICAgICAgICAgICAgICAjIGlmIHJlcG9ydD1ULCBhbiBhdXRvbWF0ZWQgcmVwb3J0IHdpbGwgYmUgY3JlYXRlZAogICAgICAgICAgICAgICAgICAgIGdlbmUyR089Z2VuZTJHTy5saXN0LAogICAgICAgICAgICAgICAgICAgIGNlbGxUeXBlQW5ub3RhdGlvbj1GQUxTRSkKYGBgCgoKCmBgYHtyfQpyLmxmeTEkc2VsZWN0UmF0aW8kbkh1YnMgKyByLmxmeTEkc2VsZWN0UmF0aW8kc3RhdHMKYGBgCkZvY3VzIG9uIDUgYW5kIDYKCmBgYHtyfQpwIDwtIGxhcHBseShyLmxmeTEkbmV0cywgZnVuY3Rpb24oY3V0b2ZmKSBjdXRvZmYkR09lbnJpY2gkcGxvdFN0YXRzKSAKCmdnYXJyYW5nZShwJGM1ICsgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMCkpLCAKICAgICAgICAgIHAkYzYgKyB0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwKSksIAogICAgICAgICAgbmNvbD0yLCBucm93PTEsIGNvbW1vbi5sZWdlbmQ9VCwgbGVnZW5kPSJib3R0b20iKQpgYGAKCmBgYHtyfQpyLmxmeTEkbmV0cyRjNSRuZXQkbW9kdWxlU2l6ZVNlbGVjdGlvblBsb3QKYGBgCgpgYGB7cn0Kci5sZnkxJG5ldHMkYzYkbmV0JG1vZHVsZVNpemVTZWxlY3Rpb25QbG90CmBgYAoKQ29ycmVsYXRpb24gd2l0aCB0cmFpdCwgSSBhc3N1bWUKYGBge3J9CnIubGZ5MSRuZXRzJGM1JG5ldCRwbG90Q29ycgpgYGAKCmBgYHtyfQpyLmxmeTEkbmV0cyRjNiRuZXQkcGxvdENvcnIKYGBgCgoKYGBge3J9CkRUOjpkYXRhdGFibGUoci5sZnkxJG5ldHMkYzUkbmV0JG1vZHVsZXMpCmBgYAoKYGBge3J9CkRUOjpkYXRhdGFibGUoci5sZnkxJG5ldHMkYzYkbmV0JG1vZHVsZXMpCmBgYAoKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi4vb3V0cHV0L1RHQ05fTEZZMS9yZXN1bHRzL0xGWTFfQUxMX2M1X1RHQ05fY3Jvc3NUYWJQbG90LnBuZyIpCmBgYAoKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi4vb3V0cHV0L1RHQ05fTEZZMS9yZXN1bHRzL0xGWTFfQUxMX2M2X1RHQ05fY3Jvc3NUYWJQbG90LnBuZyIpCmBgYAoKYGBge3J9CmdyaWQuYXJyYW5nZShyLmxmeTEkbmV0cyRjNSRHT2VucmljaCRwbG90U3RhdHMsIHIubGZ5MSRuZXRzJGM1JEdPZW5yaWNoJHBsb3ROdGVybXMsIG5yb3c9MikKYGBgCgpgYGB7cn0KZ3JpZC5hcnJhbmdlKHIubGZ5MSRuZXRzJGM2JEdPZW5yaWNoJHBsb3RTdGF0cywgci5sZnkxJG5ldHMkYzYkR09lbnJpY2gkcGxvdE50ZXJtcywgbnJvdz0yKQpgYGAKCgojIyBMRlkyIG5ldHdvcmsKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmlmKGRpci5leGlzdHMoIi4uL291dHB1dC9UR0NOX0xGWTIiKSkgICBzeXN0ZW0oInJtIC1yIC4uL291dHB1dC9UR0NOX0xGWTIiKQoKaWYoIWRpci5leGlzdHMoIi4uL291dHB1dC9UR0NOX0xGWTIiKSkgIGRpci5jcmVhdGUoIi4uL291dHB1dC9UR0NOX0xGWTIiKQoKci5sZnkyIDwtIHRlc3RBbGxDdXRvZmZzKGV4cHJEYXRhPWlucHV0X2Zvcl9MRlkyLAogICAgICAgICAgICAgICAgICAgIHRhcmdldD1MRlkyLmV4cHJlc3Npb24sCiAgICAgICAgICAgICAgICAgICAgY292cz1OVUxMLAogICAgICAgICAgICAgICAgICAgIHRyYWluLnNwbGl0PTAuNywKICAgICAgICAgICAgICAgICAgICBuZm9sZHM9NSwKICAgICAgICAgICAgICAgICAgICB0PTEwLAogICAgICAgICAgICAgICAgICAgIHBhdGg9Ii4uL291dHB1dC9UR0NOX0xGWTIiLAogICAgICAgICAgICAgICAgICAgIHRhcmdldE5hbWU9IkxGWTIiLAogICAgICAgICAgICAgICAgICAgIHRpc3N1ZU5hbWU9IkFMTCIsCiAgICAgICAgICAgICAgICAgICAgc2VlZD0zMzMzLAogICAgICAgICAgICAgICAgICAgIGN1dG9mZnM9MTA6MSwKICAgICAgICAgICAgICAgICAgICBuPTEwMCwgCiAgICAgICAgICAgICAgICAgICAgbT0xMCwgCiAgICAgICAgICAgICAgICAgICAgcz0xMCwgCiAgICAgICAgICAgICAgICAgICAgbWluQ29yPTAuMywKICAgICAgICAgICAgICAgICAgICBtYXhUb2w9MywKICAgICAgICAgICAgICAgICAgICBzYXZlPVQsCiAgICAgICAgICAgICAgICAgICAgb3ZlcndyaXRlPVQsCiAgICAgICAgICAgICAgICAgICAgYXBwcm9hY2g9ImVucmljaG1lbnQiLCAjIHRoZSBhcHByb2FjaCBzZWxlY3RlZCB0byBjb21wbGV0ZSB0aGUgc2VlZCBtb2R1bGVzCiAgICAgICAgICAgICAgICAgICAgcmVwb3J0PUYsICAgICAgICAgICAgICAjIGlmIHJlcG9ydD1ULCBhbiBhdXRvbWF0ZWQgcmVwb3J0IHdpbGwgYmUgY3JlYXRlZAogICAgICAgICAgICAgICAgICAgIGdlbmUyR089Z2VuZTJHTy5saXN0LAogICAgICAgICAgICAgICAgICAgIGNlbGxUeXBlQW5ub3RhdGlvbj1GQUxTRSkKYGBgCgoKCmBgYHtyfQpyLmxmeTIkc2VsZWN0UmF0aW8kbkh1YnMgKyByLmxmeTIkc2VsZWN0UmF0aW8kc3RhdHMKYGBgCkZvY3VzIG9uIDMKCmBgYHtyfQpwIDwtIGxhcHBseShyLmxmeTIkbmV0cywgZnVuY3Rpb24oY3V0b2ZmKSBjdXRvZmYkR09lbnJpY2gkcGxvdFN0YXRzKSAKCmdnYXJyYW5nZShwJGMzICsgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMCkpLCAKICAgICAgICAgIHAkYzQgKyB0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwKSksIAogICAgICAgICAgbmNvbD0yLCBucm93PTEsIGNvbW1vbi5sZWdlbmQ9VCwgbGVnZW5kPSJib3R0b20iKQpgYGAKCmBgYHtyfQpyLmxmeTIkbmV0cyRjMyRuZXQkbW9kdWxlU2l6ZVNlbGVjdGlvblBsb3QKYGBgCgpDb3JyZWxhdGlvbiB3aXRoIHRyYWl0LCBJIGFzc3VtZQoKYGBge3J9CnIubGZ5MiRuZXRzJGMzJG5ldCRwbG90Q29ycgpgYGAKCgpgYGB7cn0KRFQ6OmRhdGF0YWJsZShyLmxmeTIkbmV0cyRjMyRuZXQkbW9kdWxlcykKYGBgCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4uL291dHB1dC9UR0NOX0xGWTIvcmVzdWx0cy9MRlkyX0FMTF9jM19UR0NOX2Nyb3NzVGFiUGxvdC5wbmciKQpgYGAKCmBgYHtyfQpncmlkLmFycmFuZ2Uoci5sZnkyJG5ldHMkYzMkR09lbnJpY2gkcGxvdFN0YXRzLCByLmxmeTIkbmV0cyRjMyRHT2VucmljaCRwbG90TnRlcm1zLCBucm93PTIpCmBgYAoKCiMjIEhlYXQgTWFwcwoKIyMjIExGWTEKCiMjIyMgQzUKCmBgYHtyfQpMRlkxLmM1IDwtIHJlYWRfY3N2KCIuLi9vdXRwdXQvVEdDTl9MRlkxL3Jlc3VsdHMvTEZZMV9BTExfYzVfVEdDTi5jc3YiKQpMRlkxLmM1CmBgYAoKQWxsIGdlbmVzCmBgYHtyfQpyYmluZChMRlkxPUxGWTEuZXhwcmVzc2lvbixsY3BtW3VuaXF1ZShMRlkxLmM1JGdlbmVzKSxdKSAlPiUKICBhcy5tYXRyaXgoKSAlPiUKICBoZWF0bWFwLjIodHJhY2U9Im5vbmUiLCBjZXhSb3c9IDAuNiwgY2V4Q29sPTAuNywgY29sPSJibHVlcmVkIiwgc2NhbGU9InJvdyIpCmBgYApodWJnZW5lcwpgYGB7cn0KcmJpbmQoTEZZMT1MRlkxLmV4cHJlc3Npb24sbGNwbVt1bmlxdWUoTEZZMS5jNSRodWJHZW5lKSxdKSAlPiUKICBhcy5tYXRyaXgoKSAlPiUKICBoZWF0bWFwLjIodHJhY2U9Im5vbmUiLCBjZXhSb3c9IDAuNiwgY2V4Q29sPTAuNywgY29sPSJibHVlcmVkIiwgc2NhbGU9InJvdyIpCmBgYAoKIyMjIyBDNgoKYGBge3J9CkxGWTEuYzYgPC0gcmVhZF9jc3YoIi4uL291dHB1dC9UR0NOX0xGWTEvcmVzdWx0cy9MRlkxX0FMTF9jNl9UR0NOLmNzdiIpCkxGWTEuYzYKYGBgCgpBbGwgZ2VuZXMKYGBge3J9CnJiaW5kKExGWTE9TEZZMS5leHByZXNzaW9uLGxjcG1bdW5pcXVlKExGWTEuYzYkZ2VuZXMpLF0pICU+JQogIGFzLm1hdHJpeCgpICU+JQogIGhlYXRtYXAuMih0cmFjZT0ibm9uZSIsIGNleFJvdz0gMC41LCBjZXhDb2w9MC43LCBjb2w9ImJsdWVyZWQiLCBzY2FsZT0icm93IikKYGBgCmh1YmdlbmVzCmBgYHtyfQpyYmluZChMRlkxPUxGWTEuZXhwcmVzc2lvbixsY3BtW3VuaXF1ZShMRlkxLmM2JGh1YkdlbmUpLF0pICU+JQogIGFzLm1hdHJpeCgpICU+JQogIGhlYXRtYXAuMih0cmFjZT0ibm9uZSIsIGNleFJvdz0gMC42LCBjZXhDb2w9MC43LCBjb2w9ImJsdWVyZWQiLCBzY2FsZT0icm93IikKYGBgCgojIyBMRlkyIEMzCgpgYGB7cn0KTEZZMi5jMyA8LSByZWFkX2NzdigiLi4vb3V0cHV0L1RHQ05fTEZZMi9yZXN1bHRzL0xGWTJfQUxMX2MzX1RHQ04uY3N2IikKTEZZMi5jMwpgYGAKCkFsbCBnZW5lcwpgYGB7cn0KcmJpbmQoTEZZMj1MRlkyLmV4cHJlc3Npb24sbGNwbVt1bmlxdWUoTEZZMi5jMyRnZW5lcyksXSkgJT4lCiAgYXMubWF0cml4KCkgJT4lCiAgaGVhdG1hcC4yKHRyYWNlPSJub25lIiwgY2V4Um93PSAwLjYsIGNleENvbD0wLjcsIGNvbD0iYmx1ZXJlZCIsIHNjYWxlPSJyb3ciKQpgYGAKaHViZ2VuZXMKYGBge3J9CnJiaW5kKExGWTI9TEZZMi5leHByZXNzaW9uLGxjcG1bdW5pcXVlKExGWTIuYzMkaHViR2VuZSksXSkgJT4lCiAgYXMubWF0cml4KCkgJT4lCiAgaGVhdG1hcC4yKHRyYWNlPSJub25lIiwgY2V4Um93PSAwLjYsIGNleENvbD0wLjcsIGNvbD0iYmx1ZXJlZCIsIHNjYWxlPSJyb3ciKQpgYGA=